package 剑指offer;

import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangmin
 * @create 2021-12-06 9:20
 * 字符串
 */
public class day26_20_67 {
    /**==============================================================================================================================================
     * 剑指 Offer 20. 表示数值的字符串
     * 请实现一个函数用来判断字符串是否表示数值（包括整数和小数）。
     * 1、逐个匹配
     * 2、有限状态机
     * */
    //判断是否是有符号整数
    int index=0;
    public boolean isInteger(String s){
        if (index==s.length())return false;
        if (s.charAt(index)=='+'||s.charAt(index)=='-'){
            index++;
        }
        return isUnsignedInt(s);
    }
    //是否是无符号整数
    public boolean isUnsignedInt(String s){
        int start=index;
        while (index!=s.length()&&s.charAt(index)>='0'&&s.charAt(index)<='9'){
            index++;
        }
        return index>start;
    }
    // 数字的格式可以用A[.[B]][e|EC]或者.B[e|EC]表示，
    // 其中A和C都是整数（可以有正负号，也可以没有），而B是一个无符号整数
    public boolean isNumber1(String s) {
        if (s.length()==0) return false;
        index=0;
        //跳过空格
        while (index!=s.length()&&s.charAt(index)==' ') index++;
        if (index==s.length()) return false;
        //数值部分
        boolean numeric=isInteger(s);
        if (index==s.length()) return numeric;
        //判断是否是小数
        if (index!=s.length()&&s.charAt(index)=='.'){
            index++;
            // 下面一行代码用||的原因：
            // 1. 小数可以没有整数部分，例如.123等于0.123；
            // 2. 小数点后面可以没有数字，例如233.等于233.0；
            // 3. 当然小数点前面和后面可以有数字，例如233.666
            numeric=isUnsignedInt(s)||numeric;
        }
        if (index==s.length()) return numeric;
        if (index!=s.length()&&(s.charAt(index)=='e'||s.charAt(index)=='E')){
            index++;
            // 下面一行代码用&&的原因：
            // 1. 当e或E前面没有数字时，整个字符串不能表示数字，例如.e1、e1；
            // 2. 当e或E后面没有整数时，整个字符串不能表示数字，例如12e、12e+5.4
            numeric=numeric&&isInteger(s);
        }
        while (index!=s.length()&&s.charAt(index)==' ')index++;
        return numeric&&index==s.length();
    }


    //枚举字符类型
    enum CharType {
        CHAR_NUMBER, //数字
        CHAR_EXP, //e,E
        CHAR_POINT, //小数点
        CHAR_SIGN, //+，-号
        CHAR_SPACE, //空格
        CHAR_ILLEGAL //不合法字符
    }
    enum State {
        STATE_INITIAL,
        STATE_INT_SIGN,
        STATE_INTEGER,
        STATE_POINT,
        STATE_POINT_WITHOUT_INT,
        STATE_FRACTION,
        STATE_EXP,
        STATE_EXP_SIGN,
        STATE_EXP_NUMBER,
        STATE_END
    }
    public CharType toCharType(char ch) {
        if (ch >= '0' && ch <= '9') {
            return CharType.CHAR_NUMBER;
        } else if (ch == 'e' || ch == 'E') {
            return CharType.CHAR_EXP;
        } else if (ch == '.') {
            return CharType.CHAR_POINT;
        } else if (ch == '+' || ch == '-') {
            return CharType.CHAR_SIGN;
        } else if (ch == ' ') {
            return CharType.CHAR_SPACE;
        } else {
            return CharType.CHAR_ILLEGAL;
        }
    }

    public boolean isNumber(String s) {
        Map<State,Map<CharType,State>> transfer=new HashMap<>();
        Map<CharType,State> initialMap=new HashMap<CharType,State>();
        initialMap.put(CharType.CHAR_SPACE, State.STATE_INITIAL);
        initialMap.put(CharType.CHAR_NUMBER,State.STATE_INTEGER);
        initialMap.put(CharType.CHAR_POINT,State.STATE_POINT_WITHOUT_INT);
        initialMap.put(CharType.CHAR_SIGN,State.STATE_INT_SIGN);
        transfer.put(State.STATE_INITIAL,initialMap);
        Map<CharType, State> intSignMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_POINT, State.STATE_POINT_WITHOUT_INT);
        }};
        transfer.put(State.STATE_INT_SIGN, intSignMap);
        Map<CharType, State> integerMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_INTEGER);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_POINT, State.STATE_POINT);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_INTEGER, integerMap);
        Map<CharType, State> pointMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_POINT, pointMap);
        Map<CharType, State> pointWithoutIntMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
        }};
        transfer.put(State.STATE_POINT_WITHOUT_INT, pointWithoutIntMap);
        Map<CharType, State> fractionMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_FRACTION);
            put(CharType.CHAR_EXP, State.STATE_EXP);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_FRACTION, fractionMap);
        Map<CharType, State> expMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
            put(CharType.CHAR_SIGN, State.STATE_EXP_SIGN);
        }};
        transfer.put(State.STATE_EXP, expMap);
        Map<CharType, State> expSignMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
        }};
        transfer.put(State.STATE_EXP_SIGN, expSignMap);
        Map<CharType, State> expNumberMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_NUMBER, State.STATE_EXP_NUMBER);
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_EXP_NUMBER, expNumberMap);
        Map<CharType, State> endMap = new HashMap<CharType, State>() {{
            put(CharType.CHAR_SPACE, State.STATE_END);
        }};
        transfer.put(State.STATE_END, endMap);

        int len=s.length();
        State state=State.STATE_INITIAL; //初始状态
        for (int i = 0; i < len; i++) {
            CharType type=toCharType(s.charAt(i));
            if (!transfer.get(state).containsKey(type)){
                return false;
            }else {
                state=transfer.get(state).get(type);
            }
        }
        return state==State.STATE_INTEGER||state==State.STATE_POINT||state==State.STATE_FRACTION||state==State.STATE_EXP_NUMBER||state==State.STATE_END;
    }

    /**==============================================================================================================================================
     * 剑指 Offer 67. 把字符串转换成整数
     * 写一个函数 StrToInt，实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
     * */
    public int strToInt(String str) {
        int res=0;
        int bndry=Integer.MAX_VALUE/10;
        int i=0,sign=1,lengh=str.length();
        if (lengh==0) return 0;
        while (str.charAt(i)==' '){
            if (++i==lengh) return 0;
        }
        if (str.charAt(i)=='-') sign=-1;
        if (str.charAt(i)=='-'||str.charAt(i)=='+') i++;
        for (int j = i; j < lengh; j++) {
            if (str.charAt(j)<'0'||str.charAt(j)>'9') break;
            //判断是否越界：INT_MIN明明是-2147483648，但是为什么却只需要判断当前字符是否大于7，因为按常理来说当符号为负时str.charAt(j)为'8'也是不越界的，但是为什么这里只判断‘7’呢？
            // 这是因为我们如果直接向一个int变量赋值-2147483648，系统是会报错的
            if (res>bndry||res==bndry&&str.charAt(j)>'7')
                return sign==1?Integer.MAX_VALUE:Integer.MIN_VALUE;
            res=res*10+(str.charAt(j)-'0');
        }
        return sign*res;
    }
}
